package cn.takujo.takujoframework.mappingfinder;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import cn.takujo.takujoframework.common.util.ClassUtil;

/**
 * 映射搜寻器，enter进入方法
 * 
 * @author wzx
 *
 */
public class MappingFinder {

	/**
	 * 返回映射结果
	 * 
	 * @param enterClass
	 *            扫描这个类所在的包及其子包
	 * @param requestMapping
	 *            requestMapping注解
	 * @return 映射结果集合
	 */
	public static List<Mapping> enter(Class<?> enterClass, Class<? extends Annotation> requestMapping) {
		return scan(enterClass.getPackage(), requestMapping);
	}

	/**
	 * 返回映射结果，并写入到文件
	 * 
	 * @param enterClass
	 *            扫描这个类所在的包及其子包
	 * @param requestMapping
	 *            requestMapping注解
	 * @param outFile
	 *            例 d:/mapping.txt(全路径)
	 * @return 映射结果集合
	 */
	public static List<Mapping> enter(Class<?> enterClass, Class<? extends Annotation> requestMapping, String outFile) {
		List<Mapping> scan = scan(enterClass.getPackage(), requestMapping);
		File file = new File(outFile);
		File parentFile = file.getParentFile();
		if (!parentFile.exists()) {
			parentFile.mkdirs();
		}
		try {
			file.createNewFile();
			if (file.exists()) {
				FileOutputStream fo = new FileOutputStream(file);
				BufferedOutputStream bfo = new BufferedOutputStream(fo);
				for (Mapping mapping : scan) {
					String mappingUrl = mapping.getMappingUrl();
					String mappingMethod = mapping.getMappingMethod();
					String requiredParam = mapping.getRequiredParam();
					String returnValue = mapping.getReturnValue();
					String description = mapping.getDescription();
					String belongController = mapping.getBelongController();
					String line = "| url: " + mappingUrl + " | method: " + mappingMethod + " | param: " + requiredParam
							+ " | return: " + returnValue + " | controller: " + belongController + " | description: "
							+ description + " | # ";
					byte[] bytes = line.getBytes();
					bfo.write(bytes);
				}
				bfo.flush();
				bfo.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return scan;
	}

	/**
	 * 扫描注解，返回映射结果
	 * 
	 * @param pkg
	 *            被扫描包
	 * @param valueAnnotation
	 *            含有value方法的注解
	 * @return 映射结果集合
	 */
	private static List<Mapping> scan(Package pkg, Class<? extends Annotation> valueAnnotation) {
		List<Mapping> mappings = new ArrayList<>();
		List<Class<?>> list = ClassUtil.getClassesByPackage(pkg);
		if (!list.isEmpty()) {
			for (Iterator<Class<?>> iterator = list.iterator(); iterator.hasNext();) {
				Class<?> c = (Class<?>) iterator.next();
				String simpleName = c.getSimpleName();
				Findable claant = c.getAnnotation(Findable.class);
				if (claant != null) {
					String urlPrefix = "";
					Annotation re = c.getAnnotation(valueAnnotation);
					if (re != null) {
						urlPrefix = geturl(re);
					}
					String description = claant.value();
					Method[] methods = c.getMethods();
					for (Method method : methods) {
						Findable annotation = method.getAnnotation(Findable.class);
						if (annotation != null) {
							description = description + annotation.value();
						}
						injectionMapping(mappings, method, valueAnnotation, simpleName, urlPrefix, description);
					}
				} else {
					Method[] methods = c.getMethods();
					for (Method method : methods) {
						Findable annotation = method.getAnnotation(Findable.class);
						if (annotation != null) {
							String urlPrefix = "";
							Annotation re = c.getAnnotation(valueAnnotation);
							if (re != null) {
								urlPrefix = geturl(re);
							}
							injectionMapping(mappings, method, valueAnnotation, simpleName, urlPrefix,
									annotation.value());
						}
					}
				}
			}
		}
		return mappings;
	}

	/**
	 * 向集合中注入一个mapping
	 * 
	 * @param maps
	 *            集合容器
	 * @param method
	 *            方法
	 * @param valueAnnotation
	 *            含有value方法的注解
	 * @param simpleName
	 *            类名
	 * @param urlPrefix
	 *            url前缀
	 * @param description
	 *            描述
	 */
	private static void injectionMapping(List<Mapping> maps, Method method, Class<? extends Annotation> valueAnnotation,
			String simpleName, String urlPrefix, String description) {
		Annotation rm = method.getAnnotation(valueAnnotation);
		if (rm != null) {
			try {
				Mapping mapping = new Mapping();
				mapping.setBelongController(simpleName);
				mapping.setMappingMethod(method.getName());
				mapping.setReturnValue(method.getReturnType().getSimpleName());
				Class<?>[] parameterTypes = method.getParameterTypes();
				StringBuffer stringBuffer = new StringBuffer();
				for (Class<?> pac : parameterTypes) {
					stringBuffer.append(pac.getSimpleName() + ",");
				}
				int lastIndexOf = stringBuffer.lastIndexOf(",");
				if (lastIndexOf != -1) {
					stringBuffer.delete(lastIndexOf, lastIndexOf + 1);
				}
				mapping.setRequiredParam(stringBuffer.toString());
				mapping.setDescription(description);
				String url = geturl(rm);
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				if (!urlPrefix.isEmpty()) {
					mapping.setMappingUrl(urlPrefix + url);
				} else {
					mapping.setMappingUrl(url.substring(1));
				}
				maps.add(mapping);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 获取映射的url
	 * 
	 * @param re
	 * @return 返回映射的url
	 */
	private static String geturl(Annotation re) {
		if (re != null) {
			try {
				Method value = re.getClass().getMethod("value");
				String[] ss = (String[]) value.invoke(re);
				return ss[0];
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			return "";
		}
		return "";
	}

}
